package pers.qianyu.module.system.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.digest.BCrypt;
import cn.hutool.log.LogFactory;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pers.qianyu.module.core.comm.Pagination;
import pers.qianyu.module.core.constants.SysRoleConstants;
import pers.qianyu.module.core.constants.SysUserConstants;
import pers.qianyu.module.core.constants.TokenConstants;
import pers.qianyu.module.core.domain.image.entity.ImagePO;
import pers.qianyu.module.core.domain.system.converter.SysUserConverter;
import pers.qianyu.module.core.domain.system.dto.RegisterDTO;
import pers.qianyu.module.core.domain.system.dto.SysUserDTO;
import pers.qianyu.module.core.domain.system.dto.SysUserForUpdateDTO;
import pers.qianyu.module.core.domain.system.entity.SysUserPO;
import pers.qianyu.module.core.domain.system.entity.SysUserRolePO;
import pers.qianyu.module.core.domain.system.enums.SysUserGenderEnum;
import pers.qianyu.module.core.domain.system.enums.SysUserStatusEnum;
import pers.qianyu.module.core.domain.system.enums.SysUserTypeEnum;
import pers.qianyu.module.core.domain.system.query.SysUserQuery;
import pers.qianyu.module.core.domain.system.vo.SimpleUserVO;
import pers.qianyu.module.core.domain.system.vo.SysResourceVO;
import pers.qianyu.module.core.domain.system.vo.SysRoleVO;
import pers.qianyu.module.core.domain.system.vo.SysUserVO;
import pers.qianyu.module.image.dao.ImageDao;
import pers.qianyu.module.image.service.ImageCollectionService;
import pers.qianyu.module.image.service.ImageLikeService;
import pers.qianyu.module.image.service.ImageService;
import pers.qianyu.module.system.dao.SysUserDao;
import pers.qianyu.module.system.dao.SysUserRoleDao;
import pers.qianyu.module.system.exception.SysErrorCodeEnum;
import pers.qianyu.module.system.exception.SysException;
import pers.qianyu.module.system.listener.events.SysUserInfoUpdatedEvent;
import pers.qianyu.module.system.service.SysRoleResourceService;
import pers.qianyu.module.system.service.SysUserFollowService;
import pers.qianyu.module.system.service.SysUserRoleService;
import pers.qianyu.module.system.service.SysUserService;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * @author mizzle rain
 * @date 2020-11-12 14:46
 */
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {
    @Autowired
    private SysUserDao sysUserDao;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysRoleResourceService sysRoleResourceService;
    @Autowired
    private SysUserRoleDao sysUserRoleDao;

    @Autowired
    private ImageLikeService imageLikeService;
    @Autowired
    private ImageCollectionService imageCollectionService;
    @Autowired
    private SysUserFollowService sysUserFollowService;
    @Autowired
    private ImageService imageService;
    @Autowired
    private ImageDao imageDao;

    @Autowired
    private SysUserConverter sysUserConverter;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void insUser(SysUserDTO sysUserDTO) {
        // 校验用户名是否存在
        if (ifUsernameExist(sysUserDTO.getUsername())) {
            throw new SysException(SysErrorCodeEnum.USERNAME_ALREADY_EXISTS);
        }
        SysUserPO sysUserPO = sysUserConverter.convertToPO(sysUserDTO);
        sysUserPO.setPassword(BCrypt.hashpw(sysUserPO.getPassword()));
        sysUserDao.insert(sysUserPO);
    }

    @Override
    public void updUser(Long id, SysUserDTO sysUserDTO) {
        if (Objects.isNull(sysUserDao.selectById(id))) {
            throw new SysException(SysErrorCodeEnum.INVALID_SYS_USER_ID);
        }
        SysUserPO sysUserPO = sysUserConverter.convertToPO(sysUserDTO);
        sysUserPO.setId(id);
        sysUserDao.updateById(sysUserPO);
    }

    @Override
    public void updUser(SysUserForUpdateDTO sysUserForUpdateDTO) {
        Long userId = sysUserForUpdateDTO.getId();
        if (Objects.isNull(sysUserDao.selectById(userId))) {
            throw new SysException(SysErrorCodeEnum.INVALID_SYS_USER_ID);
        }
        SysUserPO sysUserPO = sysUserConverter.convertToPO(sysUserForUpdateDTO);
        sysUserDao.updateById(sysUserPO);
        applicationContext.publishEvent(new SysUserInfoUpdatedEvent(this, userId));
    }

    @Override
    public Pagination<SysUserVO> queryUser(SysUserQuery sysUserQuery) {
        Page<SysUserPO> page = new Page<>(sysUserQuery.getPage(), sysUserQuery.getLimit());
        SysUserPO sysUserPO = sysUserConverter.convertToPO(sysUserQuery);
        QueryWrapper<SysUserPO> wrapper = new QueryWrapper<>(sysUserPO);
        Page<SysUserPO> sysUserPOPage = sysUserDao.selectPage(page, wrapper);
        return sysUserConverter.convertToPagination(sysUserPOPage);
    }

    @Override
    public SysUserVO queryById(Long id) {
        SysUserPO sysUserPO = sysUserDao.selectById(id);
        if (Objects.isNull(sysUserPO)) {
            throw new SysException(SysErrorCodeEnum.INVALID_SYS_USER_ID);
        }
        // 查询该用户下的所有角色
        List<SysRoleVO> sysRoleVOS = sysUserRoleService.queryByUserId(sysUserPO.getId());

        // 查询该角色的所有资源
        ArrayList<SysResourceVO> sysResourceVOS = new ArrayList<>();
        for (SysRoleVO sysRoleVO : sysRoleVOS) {
            List<SysResourceVO> sysResourceVOList = sysRoleResourceService.queryByRoleId(sysRoleVO.getId());
            sysResourceVOS.addAll(sysResourceVOList);
        }

        List<Long> imageLikeIds = imageLikeService.findImageIdsByUserId(id);
        List<Long> imageCollectionIds = imageCollectionService.findImageIdsByUserId(id);
        Set<Long> myFollowedUserIds = sysUserFollowService.getMyFollow(id);
        return sysUserConverter.convertToVO(sysUserPO, sysRoleVOS, sysResourceVOS,
                imageLikeIds, imageCollectionIds, myFollowedUserIds);
    }

    @Override
    public SimpleUserVO querySimpleUserInfoById(Long id) {
        SysUserPO sysUserPO = sysUserDao.selectById(id);
        if (Objects.isNull(sysUserPO)) {
            LogFactory.get().info("非法用户id：{}", id);
            throw new SysException(SysErrorCodeEnum.INVALID_SYS_USER_ID);
        }
        return sysUserConverter.convertToSimpleUserVO(sysUserPO);
    }

    @Override
    public void delUser(Long id) {
        SysUserPO sysUserPO = sysUserDao.selectById(id);
        if (Objects.isNull(sysUserPO)) {
            throw new SysException(SysErrorCodeEnum.INVALID_SYS_USER_ID);
        }
        sysUserDao.deleteById(id);
        // 删除用户角色对应关系
        QueryWrapper<SysUserRolePO> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", id);
        sysUserRoleDao.delete(wrapper);
        // 删除该用户发布的所有图片
        ImagePO imagePO = new ImagePO();
        imagePO.setCreatedBy(id);
        QueryWrapper<ImagePO> queryWrapper = new QueryWrapper<>(imagePO);
        queryWrapper.select("id");
        imageDao.selectList(queryWrapper)
                .stream()
                .map(ImagePO::getId)
                .distinct()
                .forEach(imageService::delImage);
    }

    @Override
    public boolean ifUsernameExist(String username) {
        QueryWrapper<SysUserPO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        Integer count = sysUserDao.selectCount(queryWrapper);
        return count > 0;
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void register(RegisterDTO registerDTO) {
        String username = registerDTO.getUsername();
        // 判断是否包含非法用户名
        if (SysUserConstants.USERNAME_EXCEPT_LIST.contains(username)) {
            throw new SysException(SysErrorCodeEnum.INVALID_USERNAME);
        }
        // 判断用户名是否存在
        QueryWrapper<SysUserPO> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        SysUserPO po = sysUserDao.selectOne(wrapper);
        if (Objects.nonNull(po)) {
            throw new SysException(SysErrorCodeEnum.REPEATED_USERNAME);
        }

        SysUserPO sysUserPO = new SysUserPO();
        sysUserPO.setUsername(username);
        // 解密密码
        String privateKey = (String) redisTemplate.opsForValue()
                .get(TokenConstants.REGISTER_UUID_PREFIX + registerDTO.getUuid());
        RSA rsa = new RSA(privateKey, null);
        byte[] passwordBytes = Base64.decode(registerDTO.getPassword());
        byte[] decryptBytes = rsa.decrypt(passwordBytes, KeyType.PrivateKey);
        String password = new String(decryptBytes, StandardCharsets.UTF_8);
        sysUserPO.setPassword(BCrypt.hashpw(password));

        sysUserPO.setNickname(registerDTO.getNickname());
        sysUserPO.setType(SysUserTypeEnum.COMM_USER.getCode());
        sysUserPO.setEmail(registerDTO.getEmail());
        sysUserPO.setPhone("");
        sysUserPO.setRemark("");
        sysUserPO.setGender(SysUserGenderEnum.UNKNOWN.getCode());
        sysUserPO.setAvatar(SysUserConstants.DEFAULT_AVATAR);
        sysUserPO.setStatus(SysUserStatusEnum.NORMAL.getCode());

        sysUserDao.insert(sysUserPO);
        // 给用户一个普通用户的角色
        SysUserRolePO sysUserRolePO = new SysUserRolePO();
        sysUserRolePO.setUserId(sysUserPO.getId());
        sysUserRolePO.setRoleId(SysRoleConstants.COMM_USER_ID);
        if (Objects.isNull(sysUserRoleDao.selectOne(new QueryWrapper<>(sysUserRolePO)))) {
            sysUserRoleDao.insert(sysUserRolePO);
        }
        // 删除 Redis 中的键，因为注册成功之后 私匙就没用了
        redisTemplate.delete(TokenConstants.REGISTER_UUID_PREFIX);
    }
}
